<?php

class Model_Grid extends Model_Dec {

	const TABLE = "grid";
        
        //user limit of grids
        const max_grids = 500;
        
        const show_monitoring_messages = false;
	
	/**
	 * @var protected $id id value
	 * @method int get_id() get_id(void)
	 * @method void set_id() set_id(int $id)
	 */
	protected $id;
	
	/**
	 * @var protected $user_id user_id value
	 * @method int get_user_id() get_user_id(void)
	 * @method void set_user_id() set_user_id(int $user_id)
	 */
	protected $user_id;
	
	/**
	 * @var protected $creation_dt creation_dt value
	 * @method datetime get_creation_dt() get_creation_dt(void)
	 * @method void set_creation_dt() set_creation_dt(datetime $creation_dt)
	 */
	protected $creation_dt;
	
	/**
	 * @var protected $accesses accesses value
	 * @method int get_accesses() get_accesses(void)
	 * @method void set_accesses() set_accesses(int $accesses)
	 */
	protected $accesses="0";

        
	/**
	 * @return Model_Grid
	 */
	public function __construct($id=0) {
		if(0<intval($id)) {
			$db = DB::select("*")->from(self::TABLE)->where("id", "=", $id)->limit(1);
			$db = $db->as_assoc()->execute();
			if($db->count()>0) {
				$this->load($db->current());
			}
		}
	}

	protected static function get_database_fields() {
		return array(
			'user_id' => __("Grid_User_id"),
			'creation_dt' => __("Grid_Creation_dt"),
			'accesses' => __("Grid_Accesses"),
		);
	}

	public function validate() {
		$validateArray = array();
		foreach (self::get_database_fields() as $key => $value) {
			$validateArray[$key] = $this->get($key);
		}
		$validation = Validation::factory($validateArray)
				->rule('user_id', 'not_empty')
				->rule('creation_dt', 'not_empty')
				->rule('accesses', 'not_empty')
				;
		if ($validation->check()) {
			return $validateArray;
		} else {
			throw new Validation_Exception('validation', $validation);
		}
	}

	public static function get_report() {
		return DB::select(
				"id"
				, "user_id"
				, "creation_dt"
				, "accesses"
				, array("id", "actions"))
		->from(self::TABLE)
		->order_by("id", "ASC");
	}
        
        public static function delete_all_user_grids($user_id){
            $grids = DB::select("id")->from("grid")->where("user_id","=",$user_id)->execute();
            foreach($grids as $grid){
                DB::delete("grid_class")->where("grid_id","=",$grid['id'])->execute();
            }
            DB::delete("grid")->where("user_id","=",$user_id)->execute();
        }
        
        public static function generate_user_grids($user_id){
            Model_Grid::delete_all_user_grids($user_id);
            
            $user_has_institution=Model_Institution::get_user_institution($user_id);
            if(!$user_has_institution){
                return array();
            }
            
            $courses = Model_Course::get_institution_courses($user_has_institution,true);
            
            //COLOCA TODAS AS TURMAS DE CADA MATERIA LADO A LADO
            $all_classes = array();
            foreach($courses as $course){
                foreach($course['classes'] as $class){
                    $all_classes[] = new Model_Class($class["id"]);
                }
            }
            
            //para todo numero possivel de materias possivel, gera combinaçoes sem repetiçoes
            //generates combinations C(sizeof($all_classes), $i);
            $courses_with_classes = 0;
            foreach($courses as $course){
                if(sizeof($course['classes']>0)) $courses_with_classes++;
            }
            for($i=$courses_with_classes;$i>0;$i--){
                Model_Grid::generate_classes_combinations($all_classes, $i);
            }
            
            DB::update("user_actions")->set(array("last_generation"=>DB::expr("NOW()")))->where("user_id","=",$user_id)->execute();
        }
        private function set_class($class_id){
            try{
                DB::insert("grid_class",array("grid_id","class_id"))->values(array($this->id,$class_id))->execute();
            }  catch (Exception $e){
                return false;
            }
            return true;
        }
        //if valid, return the grid itself, otherwise return NULL
        public static function evaluate_grid($grid){
            foreach($grid as $i_class_from=>$compare_from_class){
                foreach($compare_from_class->class_hours as $i_time_from=>$compare_time_from){
                    foreach($grid as $i_class_to=>$compare_to_class){
                        foreach($compare_to_class->class_hours as $i_time_to=>$compare_time_to){
                            if(//testa se uma mesma turma nao possui horarios conflitantes com outras, ou si mesma
                                    (//caso o seu horario conflite com o de algum outro horario
                                        (
                                            (//mesma turma
                                                intval($compare_time_to['class_id'])==intval($compare_time_from['class_id'])//mesma turma
                                                && $compare_time_to['week_day']==$compare_time_from['week_day'] //mesmo dia
                                                && $i_time_from!=$i_time_to  //horarios diferentes 
                                             )
                                            ||
                                            (//turmas diferentes
                                                intval($compare_time_to['class_id'])!=intval($compare_time_from['class_id'])//turmas diferentes
                                                && $compare_time_to['week_day']==$compare_time_from['week_day'] //mesmo dia
                                             )
                                        )
                                        && 
                                        (//horas incompativeis
                                            (strtotime($compare_time_from['start_time']) >= strtotime($compare_time_to['start_time']) && strtotime($compare_time_from['start_time']) <= strtotime($compare_time_to['end_time']))
                                            ||
                                            (strtotime($compare_time_from['end_time']) >= strtotime($compare_time_to['start_time']) && strtotime($compare_time_from['end_time']) <= strtotime($compare_time_to['end_time']))
                                        )
                                    )
                                    ||
                                    (//caso o proprio horario seja invalido   
                                        intval($compare_time_to['class_id'])==intval($compare_time_from['class_id'])//mesma turma
                                        && $compare_time_to['week_day']==$compare_time_from['week_day'] //mesmo dia
                                        && $i_time_from==$i_time_to  //mesmo horario 
                                        && strtotime($compare_time_from['end_time']) <= strtotime($compare_time_from['start_time']) //horas invalidas
                                    )
                                    ||
                                    (//caso haja outra turma da mesma materia na grade
                                        intval($compare_from_class->get_course_id())==intval($compare_to_class->get_course_id())
                                        && intval($compare_from_class->get_id()) != intval($compare_to_class->get_id())
                                     )
                              ){
                                return NULL;
                                }
                        }
                    }
                }
            }
            return $grid;
        }
        public static function load_user_grids($user_id, $order_by="total_credits"){
            return  DB::select(
                    array("grid.id","grid_id")
                    ,array(DB::expr("COUNT(1)"),"total_classes")
                    ,array(DB::expr("SUM(class.priority)"),"total_priority")
                    ,array(DB::expr("AVG(class.priority)"),"avg_class_priority")
                    ,array(DB::expr("SUM(course.credits)"),"total_credits"))
                    ->from("class")->join("grid_class")->on("grid_class.class_id","=","class.id")
                    ->join("grid")->on("grid.id","=","grid_class.grid_id")
                    ->join("course")->on("course.id","=","class.course_id")
                    ->where("grid.user_id","=",$user_id)
                    ->group_by("grid.id");
//                    ->execute()->as_array();
        }
        private static function generate_classes_combinations(array $all_classes, $sub_set_size){
            $results = array();
            static::generate_combinations($all_classes, 0, sizeof($all_classes)-$sub_set_size+1, $sub_set_size, $results);
            return $results;
        }
        private static function generate_combinations(array $all_classes, $ini, $end, $deepness, &$results = array(), $higher_level_keys = array()){
            if(1==$deepness){
                for($i=$ini; $i<$end; $i++){
                    //evaluate the generated grid
                    $grid = $higher_level_keys;
                    $grid[] = $i;
                    //preenche array com detalhes 
                    $filled_grid = array();
                    foreach($grid as $class_index){
                        $filled_grid[] = $all_classes[$class_index];
                    }
                    $number_of_valid_grids=0;
                    if(($valid_grid = static::evaluate_grid($filled_grid))!=NULL && $number_of_valid_grids<self::max_grids){
                        //save the valid grid
                        $number_of_valid_grids++;
                        $grid = new Model_Grid();
                        $grid->set_user_id(Model_User::get_user()->get_id());
                        $grid->set_creation_dt(DB::expr("NOW()"));
                        $grid->save();
                        if(self::show_monitoring_messages) echo "grid saved<br/>";
                        //save the classes of the grid
                        foreach($valid_grid as $class){
                            $grid->set_class($class->get_id());
                            if(self::show_monitoring_messages) echo "class saved<br/>";
                            
                        }
                        unset($valid_grid);//so it does not take too much space
                    }else{
                        ob_flush(); 
                        if(self::show_monitoring_messages) echo "return null =>  grid invalida: ".implode(",",$grid)."<br/>";
                        }
                     unset($grid);
//                    $results[] = $grid;
//                    print_r($results);die("lalala");
                }
//                for($i=$ini; $i<$end; $i++){
//                    $results[] = implode(",",$higher_level_keys).",$i";
//                    }
            }else{
                for($i=$ini; $i<$end; $i++){
                    $higher_level_keys[$deepness] = $i;
                    static::generate_combinations($all_classes, $i+1, $end+1, $deepness-1, $results, $higher_level_keys);
                }
            }
        }
        public static function load_grid_html_pre_formatted($grid_id,$user_id){
            $grid = DB::select(
                    array("grid_class.class_id","class_id"),
                    array("class.identification","class_identification"),
                    array("class.course_id","course_id"),
                    array("course.code","course_code"),
                    array("course.title","course_title")
                    )
                    ->from("class")
                    ->join("grid_class")->on("grid_class.class_id","=","class.id")
                    ->join("grid")->on("grid.id","=","grid_class.grid_id")
                    ->join("course")->on("class.course_id","=","course.id")
                    ->where("grid.user_id","=",$user_id)
                    ->and_where("grid.id","=",$grid_id)
                    ->execute()->as_array();
            
            
            
            //generate an array with all the possible hours
            $hours = array();
            foreach($grid as $i=>$class){
                $grid[$i]["times"] = DB::select("*")->from("class_hours")
                        ->where("class_id","=",$class['class_id'])->execute()->as_array();
                foreach($grid[$i]["times"] as $time){
                    if(!in_array(strtotime($time['start_time']), $hours)){
                        $hours[] = strtotime($time['start_time']);
                    }
                    if(!in_array(strtotime($time['end_time']), $hours)){
                        $hours[] = strtotime($time['end_time']);
                    }
                }
            }
            //sort the hours
            array_multisort($hours);
            
            //change hours back to textual form
            foreach($hours as $i=>$hour){
                $hours[$i] = date("H:i:s",$hour);
            }
            
            $visual_grid = array();
            foreach($hours as $hour){
                $visual_grid[$hour] = array(
                    "monday"=>NULL,
                    "tuesday"=>NULL,
                    "wednesday"=>NULL,
                    "thursday"=>NULL,
                    "friday"=>NULL,
                    "saturday"=>NULL,
                    "sunday"=>NULL,
                );
            }
            
            //fill the grid to be returned with class_id values
            foreach($grid as $class){
                foreach($class['times'] as $time){
                    $passed_by_start_time = false;
                    $rowspan = 0;
                    $first_class_cell_hour_occurance = "";
                    $first_class_cell_week_day = "";
                    foreach($visual_grid as $hour_occurance=>$week_days){
                        if(!$passed_by_start_time && $hour_occurance==$time['start_time']){
                            $first_class_cell_hour_occurance = $hour_occurance;
                            $first_class_cell_week_day = $time['week_day'];
                            $passed_by_start_time=true;
                            $rowspan++;
                        }else if($passed_by_start_time && $hour_occurance!=$time['end_time']){
                            $visual_grid[$hour_occurance][$time['week_day']] = "blank";
                            $rowspan++;
                        }else if($hour_occurance==$time['end_time']){
                            break;
                        }
                    }
                    $class_infos = array(
                        "class_id"=>$time["class_id"],
                        "class_identification"=>$class["class_identification"],
                        "course_id"=>$class["course_id"],
                        "course_code"=>$class["course_code"],
                        "course_title"=>$class["course_title"],
                        "rowspan"=>$rowspan,
                    );
                    $visual_grid[$first_class_cell_hour_occurance][$first_class_cell_week_day] = $class_infos;
                }
            }
            
            //set rowspans for blank columns
            $week_days = array(
                "monday",
                "tuesday",
                "wednesday",
                "thursday",
                "friday",
                "saturday",
                "sunday",
            );
            foreach($week_days as $week_day){
                $rowspan =0;
                $blank_sequence_index_start = "";
                foreach($visual_grid as $hour_occurance=>$visual_week_days){
//                    try{
                    if($visual_week_days[$week_day]==NULL){
                        if($blank_sequence_index_start==""){//means its the first of the series
                            $blank_sequence_index_start = $hour_occurance;
                        }
                        $rowspan++;
                    }else{//end of the sequence made by a new class
                        if($blank_sequence_index_start!=""){
                            $visual_grid[$blank_sequence_index_start][$week_day] = $rowspan;
                            $rowspan=0;
                            $blank_sequence_index_start="";
                        }
                    }
//                    }catch(Exception $e){
//                        $visual_week_days["hour"]=$hour_occurance; print_r($visual_week_days);die();
//                    }
                }
                if($blank_sequence_index_start!=""){
                    $visual_grid[$blank_sequence_index_start][$week_day] = $rowspan;
                }
            }
            return $visual_grid;
        }
        
}